package tech.yixiyun.framework.kuafu.domain;

import cn.hutool.core.util.ClassUtil;
import tech.yixiyun.framework.kuafu.domain.annotation.Column;
import tech.yixiyun.framework.kuafu.domain.annotation.ColumnType;
import tech.yixiyun.framework.kuafu.domain.annotation.GenerateFrom;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.sql.Time;
import java.util.Date;
import java.util.Objects;

/**
 * 列定义
 */
public class ColumnDefinition {



    /**
     * 对应的类字段
     */
    private Field field;

    /**
     * 列名
     */
    private String name;

    /**
     * 指定字段在数据库中的类型
     */
    private ColumnType type;
    /**
     * 指定字段的长度
     */
    private int length;
    /**
     * 如果字段是decimal类型，通过它来指定小数位数，
     */
    private int precision;
    /**
     * 字段注释
     */
    private String comment;
    /**
     * 是否是主键
     */
    private boolean isPrimaryKey;

    /**
     * 主键赋值方式，只有在该字段是主键字段时有效。
     */
    private GenerateFrom generateFrom;

    /**
     * 自动填充0
     */
    private boolean zerofill;

    /**
     * 是否指定字段必须notnull
     */
    private boolean notNull;
    /**
     * 指定字段默认值，当字段未指定notnull时
     */
    private String defaultValue;

    /**
     * 是否无符号，只适合数字类型字段，默认false
     */
    private boolean unsigned;

    /**
     * 标记该字段是否是临时字段，只参与query类型sql结果的封装，但不参与增删改类型sql执行过程，表构建时也忽略该字段。<Br/>
     * 多用于联表查询时，存放其他表数据。
     */
    private boolean isTemp;
    /**
     * 是否需要对字段内容做xss处理，防止xss攻击。主要是通过XSSKit的process方法处理。该方法会将:<br/>
     * 1. 将&lt;script 的< 替换成全角字符<br/>
     * 2. 将eval() 的 ( 替换成中文<br/>
     * 3. 将javascript: 的 : 替换成中文<br/>
     * 4. 将vbscript: 的 : 替换成中文<br/>
     * 5. 将expression() 的 ( 替换成中文<br/>
     *
     * @return
     */
    private boolean xss;

    /**
     *
     * @param field
     * @param column 字段上的@Column注解，没有就传入null
     */
    public ColumnDefinition(Field field, Column column) {
        field.setAccessible(true);
        this.field = field;
        this.name = (column == null || Objects.equals(column.name(), "") )? field.getName() : column.name();
        this.isTemp = column == null ? false : column.isTemp();
        if (isTemp == false) {
            checkType(field, column);
            checkLength(column);
            checkGenerateFrom(column);
            this.precision = column == null ? 4 : column.precision();
            this.comment = column == null ? "" : column.comment();
            this.isPrimaryKey = column == null ? false : column.isPrimary();
            this.zerofill = column == null ? false : column.zerofill();
            this.notNull = column == null ? false : column.notNull();
            this.xss = column == null ? true : column.xss();
        }
        //如果是基本数据类型，那么默认值需要是 0
        if ((column == null || "null".equals(column.defaultValue())) && field.getType().isPrimitive()) {
            this.defaultValue = "0";
        } else {
            this.defaultValue = column == null ? "null" : column.defaultValue();
        }

        this.unsigned = column == null ? false : column.unsigned();
    }




    /**
     * 检测字段的生成类型，如果字段是主键才会应用
     * @param column
     */
    private void checkGenerateFrom(Column column) {
        if (column != null && column.generateFrom() != GenerateFrom.DEFAULT) {
            this.generateFrom = column.generateFrom();
            Class<?> type = this.field.getType();
            if (this.generateFrom == GenerateFrom.SNOWFLAKE && type != Long.class && type != long.class && type != String.class) {
                throw new DomainException("解析Domain类【"+this.field.getDeclaringClass()+"】发生异常：雪花算法仅长整数类型或者字符类型字段");
            }
        } else {
            switch (this.type) {
                case VARCHAR:
                    this.generateFrom = GenerateFrom.SNOWFLAKE;
                    break;
                case INT:
                    this.generateFrom = GenerateFrom.AUTOINCREMENT;
                    break;
                case BIGINT:
                    this.generateFrom = GenerateFrom.SNOWFLAKE;
                    break;
                case CHAR:
                    this.generateFrom = GenerateFrom.SNOWFLAKE;
                    break;
                default:
                    this.generateFrom = GenerateFrom.SIGNED;
            }
        }
    }


    /**
     * 字段字段长度
     * @param column
     */
    private void checkLength(Column column) {
        if (column != null && column.length() > 0) {
            this.length = column.length();
            return;
        }
        switch (this.type) {
            case VARCHAR:
                this.length = 256;
                break;
            case INT:
                this.length = 11;
                break;
            case DECIMAL:
                this.length = 20;
                break;
            case CHAR:
                this.length = 256;
                break;
            case BIGINT:
                this.length = 20;
                break;

        }
    }

    /**
     * 检测字段类型
     * @param field
     * @param column
     * @return
     */
    private void checkType(Field field, Column column) {
        ColumnType columnType = null;
        Type type = field.getType();
        Class clazz = (Class)type;

        if (column != null && column.type() != ColumnType.DEFAULT) {
            columnType = column.type();
        } else if (clazz == int.class || clazz == Integer.class) {
            columnType = ColumnType.INT;
        } else if (clazz == double.class || clazz == Double.class) {
            columnType = ColumnType.DECIMAL;
        } else if (clazz == String.class) {
            columnType = ColumnType.VARCHAR;
        } else if (clazz == boolean.class || clazz == Boolean.class) {
            columnType = ColumnType.BIT;
        } else if (clazz == Date.class) {
            columnType = ColumnType.DATETIME;
        } else if (clazz == long.class || clazz == Long.class) {
            columnType = ColumnType.BIGINT;
        } else if(clazz == java.sql.Date.class) {
            columnType = ColumnType.DATE;
        }else if (clazz == Time.class) {
            columnType = ColumnType.TIME;
        } else if(clazz.isArray()) {
            Class componentType = clazz.getComponentType();
            if ( componentType == byte.class) {
                columnType = ColumnType.BLOB;
            } else if (componentType == String.class || componentType.isPrimitive() || ClassUtil.isPrimitiveWrapper(componentType)) {
                columnType = ColumnType.VARCHAR;
            } else {
                columnType = columnType.JSON;
            }
        } else if(clazz.isEnum()) {
            columnType = ColumnType.VARCHAR;
        } else if (clazz == byte.class || clazz == Byte.class) {
            columnType = ColumnType.TINYINT;
        } else if (clazz == short.class || clazz == Short.class) {
            columnType = ColumnType.SMALLINT;
        } else if (clazz == float.class || clazz == Float.class) {
            columnType = ColumnType.FLOAT;
        } else {
            columnType = ColumnType.JSON;
        }
//        if (columnType == null) {
//            throw new DomainException(field.getDeclaringClass().getName() + "里有无法自动匹配数据库字段类型的的成员变量：" + field.getName()
//                    + "，请通过@Column手动指定其type，如果不想映射数据库字段，请将isTemp标记为true");
//        }

        this.type = columnType;


    }

    public Field getField() {
        return field;
    }

    public void setField(Field field) {
        this.field = field;
    }

    public ColumnType getType() {
        return type;
    }

    public void setType(ColumnType type) {
        this.type = type;
    }

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public int getPrecision() {
        return precision;
    }

    public void setPrecision(int precision) {
        this.precision = precision;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public boolean getIsPrimaryKey() {
        return isPrimaryKey;
    }

    public void setIsPrimaryKey(boolean primaryKey) {
        isPrimaryKey = primaryKey;
    }

    public GenerateFrom getGenerateFrom() {
        return generateFrom;
    }

    public void setGenerateFrom(GenerateFrom generateFrom) {
        this.generateFrom = generateFrom;
    }

    public boolean getIsZerofill() {
        return zerofill;
    }

    public void setIsZerofill(boolean zerofill) {
        this.zerofill = zerofill;
    }

    public boolean getIsNotNull() {
        return notNull;
    }

    public void setIsNotNull(boolean notNull) {
        this.notNull = notNull;
    }

    public String getDefaultValue() {
        return defaultValue;
    }

    public void setDefaultValue(String defaultValue) {
        this.defaultValue = defaultValue;
    }

    public boolean getIsUnsigned() {
        return unsigned;
    }

    public void setIsUnsigned(boolean unsigned) {
        this.unsigned = unsigned;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean getIsTemp() {
        return isTemp;
    }

    public void setIsTemp(boolean temp) {
        isTemp = temp;
    }

    public boolean isXss() {
        return xss;
    }

    public void setXss(boolean xss) {
        this.xss = xss;
    }
}
